<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>vue项目前端解决xss攻击</title>
</head>
<body>
  
</body>
<script>
// 1.对于从接口请求的数据，尽量使用{{}}加载,而不是V-HTML
// vue中的大括号会把数据解释为普通文本。通常如果要解释成html代码则要用v-html。而此指令相当于innerHTML。虽然像innerHTML一样不会直接输出script标签，但也可以输出img，iframe等标签。

// vue文档关于v-html的说明如下所示：

// 2.对用V-HTML和INNERHTML加载的客户信息进行转义
// 如果显示内容里面有html片段，一定需要用v-html或者innerHTML加载，例如：
<div v-html="`<span>${message}</span>`"></div>
里面的message是客户自己输入的信息，如果此时是恶意的dom片段肯定会引起XSS攻击。此时我们可以对“<”，">"转译成“<”，“>”。然后再输入到页面。


// 可以使用lodash里面的escape方法对客户信息进行转译。如下：
import _escape from 'lodash/escape'
Vue.prototype.$escape = _escape

在vue中插入一个全局方法，对需要转译的数据就使用这个方法：
<div v-html="`<span>${$escape(message)}</span>`"></div>

3.在入口页面的META中使用CSP
在入口文件的head添加meta标签
<meta http-equiv="Content-Security-Policy" content="script-src 'self';style-src 'self'">
<meta http-equiv="Content-Security-Policy" content="style-src 'self' 'unsafe-inline';script-src 'self' 'unsafe-inline' 'unsafe-eval' https://webapi.amap.com https://restapi.amap.com https://vdata.amap.com https://appx/web-view.min.js;worker-src blob:">
该指令说明：允许自身css、js和高德地图api、地图数据。

上面的CSP设置表示，script脚本资源和style样式资源只能加载当前域名下的资源。这样子可以避免外部恶意的脚本的加载和执行。

如果页面有例如下面的标签，那这些CDN资源是加载不了的。
<link href="https://cdn.bootcdn.net/ajax/libs/font-awesome/1.0.0/css/font-awesome.css" rel="stylesheet">
<script src='https://cdn.bootcdn.net/ajax/libs/angular.js/0.10.0/angular-ie-compat.js'></script>
题外话：个人不倡议用第三方CDN，其一是不会减少页面加载资源的体积，其二是第三方CDN稳定性不能保证，有时候第三方CDN的服务器会挂掉导致需要的资源加载不了。

一般会用下面的CSP配置：
<meta http-equiv="Content-Security-Policy" content="script-src 'self' 'unsafe-eval';style-src 'self' 'unsafe-inline'">
设置解释：

script-src:只加载当面域名服务器下的资源，且允许eval执行脚本。因为webpack在development模式下大量使用eval进行脚本注入，且在development中常用的souce-map是cheap-module-eval-source-map。如果script-src设置成‘self’会阻止eval的使用。
style-src:只加载当面域名服务器下的资源，且允许使用内联资源。有时候无论在开发环境还是生产环境，可能都是通过webpack打包把CSS内容打包到JS文件里面。加载页面时，JS脚本会在页面中插入一个个style标签补充层叠样式模型。如果style-src设置成‘self’会阻止style内联样式的插入和执行。
4.针对特殊场景，选择性过滤XSS标签
在项目中，XSS的安全漏洞很容易出现，例如在聊天模块和富文本模块很容易出现。

有时候你想实现富文本编辑器里编辑html内容的业务。可是又担心XSS恶意脚本的注入。此时可以使用一个xss工具。网址：https://github.com/leizongmin/js-xss。更详细的用法可以看附上的网址，这里简单说一下用法。
首先下载xss
npm i xss -S

（1）在页面中引入资源且生成XSS过滤器，对内容进行过滤

var xss = require("xss") 
const option={} //自定义设置
const myxss = new xss.FilterXSS(option);
const line='<script type="text/javascript">alert(1);</script>'
var html = myxss.process(line);
console.log(html); //输出：&lt;script type="text/javascript"&gt;alert(1);&lt;/script&gt;

（2）如果我想不过滤img标签的onerror属性，或者不过滤style标签。通过设置whiteList可选择性的保留特定标签及其属性，例如：
const option={
    whiteList:{
        img:['src','onerror'] //img标签保留src,onerror属性
        style:['type'] //style标签默认是不在whileList属性里的，现在添加上去
    }
}
const myxss = new xss.FilterXSS(option);
letline='<img src="./123.png" onerror="alert(1);" alt="123">'
let html = myxss.process(line);
console.log(html); //输出：<img src="./123.png" onerror="alert(1);">
line='<style type="text/css">color:white;</style>'
html = myxss.process(line);
console.log(html); //输出：<style type="text/css">color:white;</style>
xss默认的whiteList可以通过console.log(xss.whiteList)显示。

（3）如果想彻底过滤掉类似script,noscript标签，option可如下设置：
const option={
    stripIgnoreTagBody: ["script","noscript"],
}
const myxss = new xss.FilterXSS(option)
let line='<script type="text/javascript">alert(1);</script>'
let html = myxss.process(line)
console.log(html.length) //输出0,即html被转化为空字符串
line='<noscript>123</noscript>'
html = myxss.process(line)
console.log(html.length) //输出0,即html被转化为空字符串
stripIgnoreTagBody用于设置不在whiteList的标签的过滤方法。例如script，不在whiteList会被执行xss内部的escapeHtml方法。如一开头的例子会把“<”，“>”进行转义。但如果stripIgnoreTagBody中添加了script。则会直接把整个script标签过滤掉。

（4）xss默认生成的过滤器是会过滤掉任何标签的class属性。如果我们不想过滤任何在whiteList的标签的class属性，可以这么设置：
const option={
    onIgnoreTagAttr: function(tag, name, value, isWhiteAttr) {
        if (['style','class'].includes(name)) {
            return `${name}="${xss.escapeAttrValue(value)}"`
        }
    },
}
const myxss = new xss.FilterXSS(option);
let line='<div class="box"></div>'
let html = myxss.process(line);
console.log(html); //输出：<div class="box"></div>
onIgnoreAttr方法用于设置白名单中特定属性的过滤方法。




  // 项目中input框中添加验证方法
  // 通过下面正则表达式验证
export function isSpecialCharacter (s) {
  let regEn = /[`~!@#$%^&*()_+<>?:"{}.\/;'[\]]/im
  let regCn = /[·！#￥（——）：；“”‘、|《。》？、【】[\]]/im
 
  if (regEn.test(s) || regCn.test(s)) {
    return true;
  } else {
    return false;
  }
}
// 调用方法  判断是否满足条件
export function validateSpecialCharacters (rule, value, callback) {
  if (!value) {
    callback()
  } else {
    if (isSpecialCharacter(value)) {
      callback('请不要输入特殊符号!')
    } else if(!value.trim().length) {
      callback("请不要只输入空格!")
    } else{
      callback()
    }
  }
}

// 调用上面写好的方法
rules: {
    years: [{ required: true, message: '请选择获奖年份', trigger: 'change' }],
    accountType:[{ required: true, message: '请选择核算类型', trigger: 'change' }],
    emplName:[{ validator:validateSpecialCharacters }]
},

// 下面式textarea中防止xss攻击解决方法
// 1.	安装vue-xss组件
// cnpm install --save vue-xss

// 2.	Main.js引入
// import VueXss from 'vue-xss'
// Vue.use(VueXss)

// 3.	页面模块全局使用
// <div li v-html="$xss(content)"> </div>  // dom渲染，将&gt;输出<>标签
// this.$xss('<script>alert("xss");</' + 'script>')  // js将<>转义成&gt;

</script>
</html>